/**
 * 文章内容抓取工具
 * 用于获取RSS文章的完整内容并进行清理和格式化
 */

import DOMPurify from 'dompurify';
import { getProxyImageUrl } from './imageProxy';

/**
 * 内容抓取配置接口
 * @interface ContentFetchConfig
 */
interface ContentFetchConfig {
  /** 是否移除广告 */
  removeAds?: boolean;
  /** 是否移除脚本 */
  removeScripts?: boolean;
  /** 是否保留图片 */
  keepImages?: boolean;
  /** 是否保留链接 */
  keepLinks?: boolean;
  /** 最大内容长度 */
  maxLength?: number;
}

/**
 * 内容抓取结果接口
 * @interface ContentFetchResult
 */
interface ContentFetchResult {
  /** 是否成功 */
  success: boolean;
  /** 清理后的内容 */
  content?: string;
  /** 错误信息 */
  error?: string;
  /** 原始URL */
  originalUrl?: string;
  /** 内容长度 */
  contentLength?: number;
}

/**
 * 文章内容抓取器类
 * @class ContentFetcher
 */
export class ContentFetcher {
  private readonly defaultConfig: ContentFetchConfig = {
    removeAds: true,
    removeScripts: true,
    keepImages: true,
    keepLinks: true,
    maxLength: 50000,
  };

  /**
   * 构造函数
   * @param {ContentFetchConfig} config - 配置选项
   */
  constructor(private config: ContentFetchConfig = {}) {
    this.config = { ...this.defaultConfig, ...config };
  }

  /**
   * 抓取文章内容
   * @param {string} url - 文章URL
   * @returns {Promise<ContentFetchResult>} 抓取结果
   */
  async fetchContent(url: string): Promise<ContentFetchResult> {
    try {
      console.log(`开始抓取文章内容: ${url}`);
      
      // 首先尝试使用本地代理服务器
      try {
        const proxyUrl = `/api/proxy?url=${encodeURIComponent(url)}`;
        
        const response = await fetch(proxyUrl, {
          method: 'GET',
          headers: {
            'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
          },
          signal: AbortSignal.timeout(15000), // 15秒超时
        });

        if (!response.ok) {
          throw new Error(`代理服务器错误: ${response.status} ${response.statusText}`);
        }

        const html = await response.text();
        console.log(`通过本地代理获取HTML成功，长度: ${html.length}`);

        // 解析和清理内容
        const cleanContent = this.extractAndCleanContent(html, url);
        
        return {
          success: true,
          content: cleanContent,
          originalUrl: url,
          contentLength: cleanContent.length,
        };
      } catch (proxyError) {
        console.warn('本地代理抓取失败，使用降级方案:', proxyError);
        
        // 降级方案：返回基本的错误信息和建议
        const fallbackContent = `
          <div class="content-fetch-error">
            <h3>内容获取失败</h3>
            <p>由于网络限制，无法获取完整的文章内容。</p>
            <p>您可以点击下方链接访问原文：</p>
            <a href="${url}" target="_blank" rel="noopener noreferrer" class="text-blue-500 hover:text-blue-700 underline">
              查看原文
            </a>
          </div>
        `;
        
        return {
          success: false,
          content: fallbackContent,
          error: proxyError instanceof Error ? proxyError.message : '网络错误',
          originalUrl: url,
          contentLength: fallbackContent.length,
        };
      }
    } catch (error) {
      console.error(`抓取文章内容失败: ${url}`, error);
      
      const errorContent = `
        <div class="content-fetch-error">
          <h3>内容获取失败</h3>
          <p>抱歉，无法获取文章内容。</p>
          <p>错误信息：${error instanceof Error ? error.message : '未知错误'}</p>
          <a href="${url}" target="_blank" rel="noopener noreferrer" class="text-blue-500 hover:text-blue-700 underline">
            查看原文
          </a>
        </div>
      `;
      
      return {
        success: false,
        content: errorContent,
        error: error instanceof Error ? error.message : '未知错误',
        originalUrl: url,
        contentLength: errorContent.length,
      };
    }
  }

  /**
   * 提取和清理文章内容
   * @param {string} html - 原始HTML
   * @param {string} url - 原始URL
   * @returns {string} 清理后的内容
   */
  private extractAndCleanContent(html: string, url: string): string {
    try {
      // 创建DOM解析器
      const parser = new DOMParser();
      const doc = parser.parseFromString(html, 'text/html');

      // 尝试提取主要内容
      let contentElement = this.findMainContent(doc);
      
      if (!contentElement) {
        console.warn('未找到主要内容区域，使用body作为备选');
        contentElement = doc.body;
      }

      // 清理内容
      this.cleanElement(contentElement);

      // 处理相对链接
      this.processRelativeLinks(contentElement, url);

      // 获取清理后的HTML
      let cleanHtml = contentElement.innerHTML;

      // 使用DOMPurify进行最终清理
      cleanHtml = DOMPurify.sanitize(cleanHtml, {
        ALLOWED_TAGS: [
          'p', 'div', 'span', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
          'strong', 'b', 'em', 'i', 'u', 'strike', 'del',
          'ul', 'ol', 'li', 'blockquote', 'pre', 'code',
          'img', 'a', 'br', 'hr', 'table', 'thead', 'tbody', 'tr', 'td', 'th'
        ],
        ALLOWED_ATTR: [
          'href', 'src', 'alt', 'title', 'class', 'id',
          'target', 'rel', 'width', 'height'
        ],
        KEEP_CONTENT: true,
      });

      // 限制内容长度
      if (this.config.maxLength && cleanHtml.length > this.config.maxLength) {
        cleanHtml = cleanHtml.substring(0, this.config.maxLength) + '...';
      }

      return cleanHtml;
    } catch (error) {
      console.error('内容提取和清理失败:', error);
      return '<p>内容解析失败</p>';
    }
  }

  /**
   * 查找主要内容区域
   * @param {Document} doc - 文档对象
   * @returns {Element | null} 主要内容元素
   */
  private findMainContent(doc: Document): Element | null {
    // 常见的内容选择器
    const contentSelectors = [
      'article',
      '[role="main"]',
      '.article-content',
      '.post-content',
      '.entry-content',
      '.content',
      '.main-content',
      '#content',
      '#main',
      '.article-body',
      '.post-body',
      '.story-body',
      '.article-text',
    ];

    for (const selector of contentSelectors) {
      const element = doc.querySelector(selector);
      if (element && element.textContent && element.textContent.trim().length > 100) {
        console.log(`找到主要内容区域: ${selector}`);
        return element;
      }
    }

    // 如果没有找到，尝试找到最大的文本块
    const allDivs = doc.querySelectorAll('div, section, main');
    let maxTextLength = 0;
    let bestElement: Element | null = null;

    allDivs.forEach(element => {
      const textLength = element.textContent?.trim().length || 0;
      if (textLength > maxTextLength && textLength > 200) {
        maxTextLength = textLength;
        bestElement = element;
      }
    });

    if (bestElement) {
      console.log(`通过文本长度找到主要内容区域，长度: ${maxTextLength}`);
    }

    return bestElement;
  }

  /**
   * 清理元素内容
   * @param {Element} element - 要清理的元素
   */
  private cleanElement(element: Element): void {
    // 移除不需要的元素
    const unwantedSelectors = [
      'script',
      'style',
      'noscript',
      'iframe',
      'embed',
      'object',
      '.advertisement',
      '.ads',
      '.ad',
      '.social-share',
      '.comments',
      '.comment',
      '.sidebar',
      '.navigation',
      '.nav',
      '.header',
      '.footer',
      '.related-posts',
      '.popup',
      '.modal',
      '.overlay',
      // 新增：移除导航栏相关元素
      '.cv',
      '.breadcrumb',
      '.breadcrumbs',
      // 新增：移除文章元信息
      '.l',
      '.r',
      '.article-meta',
      '.post-meta',
      '.meta-info',
      '#pubtime_baidu',
      '#source_baidu',
      '#author_baidu',
      '#editor_baidu',
      '.pti_comm',
      // 新增：移除相关文章和标签
      '.hot_tags',
      '.tags',
      '.related-articles',
      '.related-content',
      '.article-tags',
      // 新增：移除评论相关
      '[href="#post_comm"]',
      '.comment-link',
      '.comments-link',
      // 新增：移除错误报告和投诉元素
      '.newserror',
      '.error-report',
      '.complaint',
      '.report-error',
      // 新增：移除其他不需要的内容
      '.share-buttons',
      '.social-buttons',
      '.author-info',
      '.article-footer',
      '.post-footer',
    ];

    if (this.config.removeAds) {
      unwantedSelectors.push(
        '[class*="ad"]',
        '[id*="ad"]',
        '[class*="banner"]',
        '[class*="sponsor"]'
      );
    }

    unwantedSelectors.forEach(selector => {
      const elements = element.querySelectorAll(selector);
      elements.forEach(el => el.remove());
    });

    // 移除包含特定文本的元素
    const textBasedRemovals = [
      { selector: 'h2', text: '相关文章' },
      { selector: 'h2', text: '相关阅读' },
      { selector: 'h2', text: '推荐阅读' },
      { selector: 'h3', text: '相关文章' },
      { selector: 'h3', text: '相关阅读' },
      { selector: 'div', text: '关键词：' },
      { selector: 'span', text: '关键词：' },
      { selector: 'div', text: '评论：' },
      { selector: 'a', text: '评论：' },
    ];

    textBasedRemovals.forEach(({ selector, text }) => {
      const elements = element.querySelectorAll(selector);
      elements.forEach(el => {
        if (el.textContent && el.textContent.includes(text)) {
          // 如果是相关文章标题，也移除其后的列表
          if (text.includes('相关') && (selector === 'h2' || selector === 'h3')) {
            let nextElement = el.nextElementSibling;
            while (nextElement) {
              if (nextElement.tagName === 'UL' || nextElement.tagName === 'OL' || 
                  nextElement.classList.contains('related') ||
                  nextElement.classList.contains('article-list')) {
                const toRemove = nextElement;
                nextElement = nextElement.nextElementSibling;
                toRemove.remove();
              } else {
                break;
              }
            }
          }
          el.remove();
        }
      });
    });

    // 移除包含特定类名模式的元素
    const classPatterns = [
      /.*related.*/i,
      /.*recommend.*/i,
      /.*suggest.*/i,
      /.*tags?.*/i,
      /.*meta.*/i,
      /.*info.*/i,
      /.*breadcrumb.*/i,
      /.*nav.*/i,
      /.*comment.*/i,
    ];

    const allElements = element.querySelectorAll('*');
    allElements.forEach(el => {
      const className = el.className;
      if (typeof className === 'string') {
        const shouldRemove = classPatterns.some(pattern => pattern.test(className));
        if (shouldRemove) {
          el.remove();
        }
      }
    });

    // 处理图片
    if (!this.config.keepImages) {
      const images = element.querySelectorAll('img');
      images.forEach(img => img.remove());
    } else {
      // 为图片添加懒加载和错误处理，优先使用data-original并通过代理处理
      const images = element.querySelectorAll('img');
      images.forEach(img => {
        // 优先使用data-original中的高质量图片URL
        const dataOriginal = img.getAttribute('data-original');
        const currentSrc = img.getAttribute('src');
        
        // 选择最佳的图片URL
        const bestImageUrl = dataOriginal || currentSrc;
        
        if (bestImageUrl) {
          // 通过图片代理处理URL
          const proxyUrl = getProxyImageUrl(bestImageUrl);
          img.setAttribute('src', proxyUrl);
          
          // 移除data-original属性（因为已经使用了）
          if (dataOriginal) {
            img.removeAttribute('data-original');
          }
        }
        
        img.setAttribute('loading', 'lazy');
        img.style.maxWidth = '100%';
        img.style.height = 'auto';
        
        // 添加错误处理
        img.onerror = () => {
          console.warn('图片加载失败:', img.src);
          // 如果有备用的src，可以尝试使用
          if (dataOriginal && currentSrc && img.src !== getProxyImageUrl(currentSrc)) {
            img.src = getProxyImageUrl(currentSrc);
          }
        };
      });
    }

    // 处理链接
    if (!this.config.keepLinks) {
      const links = element.querySelectorAll('a');
      links.forEach(link => {
        const span = document.createElement('span');
        span.innerHTML = link.innerHTML;
        link.parentNode?.replaceChild(span, link);
      });
    } else {
      // 为外部链接添加target="_blank"
      const links = element.querySelectorAll('a');
      links.forEach(link => {
        const href = link.getAttribute('href');
        if (href && (href.startsWith('http') || href.startsWith('//'))) {
          link.setAttribute('target', '_blank');
          link.setAttribute('rel', 'noopener noreferrer');
        }
      });
    }

    // 移除空元素
    const emptyElements = element.querySelectorAll('p:empty, div:empty, span:empty');
    emptyElements.forEach(el => el.remove());
  }

  /**
   * 处理相对链接
   * @param {Element} element - 要处理的元素
   * @param {string} baseUrl - 基础URL
   */
  private processRelativeLinks(element: Element, baseUrl: string): void {
    try {
      const base = new URL(baseUrl);
      
      // 处理图片链接，包括data-original属性
      const images = element.querySelectorAll('img');
      images.forEach(img => {
        // 处理src属性
        const src = img.getAttribute('src');
        if (src && !src.startsWith('http') && !src.startsWith('data:')) {
          try {
            const absoluteUrl = new URL(src, base).href;
            img.setAttribute('src', absoluteUrl);
          } catch (error) {
            console.warn(`无法处理图片链接: ${src}`, error);
          }
        }
        
        // 处理data-original属性
        const dataOriginal = img.getAttribute('data-original');
        if (dataOriginal && !dataOriginal.startsWith('http') && !dataOriginal.startsWith('data:')) {
          try {
            const absoluteUrl = new URL(dataOriginal, base).href;
            img.setAttribute('data-original', absoluteUrl);
          } catch (error) {
            console.warn(`无法处理data-original链接: ${dataOriginal}`, error);
          }
        }
      });

      // 处理超链接
      const links = element.querySelectorAll('a');
      links.forEach(link => {
        const href = link.getAttribute('href');
        if (href && !href.startsWith('http') && !href.startsWith('#') && !href.startsWith('mailto:')) {
          try {
            const absoluteUrl = new URL(href, base).href;
            link.setAttribute('href', absoluteUrl);
          } catch (error) {
            console.warn(`无法处理链接: ${href}`, error);
          }
        }
      });
    } catch (error) {
      console.error('处理相对链接失败:', error);
    }
  }
}

/**
 * 创建默认的内容抓取器实例
 */
export const defaultContentFetcher = new ContentFetcher();

/**
 * 抓取文章内容的便捷函数
 * @param {string} url - 文章URL
 * @param {ContentFetchConfig} config - 配置选项
 * @returns {Promise<ContentFetchResult>} 抓取结果
 */
export async function fetchArticleContent(
  url: string, 
  config?: ContentFetchConfig
): Promise<ContentFetchResult> {
  const fetcher = config ? new ContentFetcher(config) : defaultContentFetcher;
  return await fetcher.fetchContent(url);
}